home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Die Speccy' 97
/
Die Speccy' 97.iso
/
amiga_system
/
the_aminet
/
comm
/
misc
/
8n1.lha
/
8n1.s
< prev
next >
Wrap
Text File
|
1995-09-19
|
91KB
|
1,934 lines
** $Revision Header Start *****************************************************
** **
** Name : 8n1.s **
** Copyright : ⌐ Copyright 95 **
** Author : L.Lucius **
** Created : 10 Jul 95 **
** Version : 37.13 **
** Translator : snma 2.00 17-Sep-95 **
** **
** Date Version Comment **
** --------- ------- ---------------------------------------------------- **
** 19 Sep 95 37.13 Fourth Aminet release. **
** 07 Sep 95 37.13 Store flag byte after setting in AbortIO. **
** 02 Sep 95 37.12 Third Aminet release. **
** 02 Sep 95 37.12 Changed DS alignment directives to CNOP directives. **
** 29 Aug 95 37.12 Added test for QUICK bit in AbortIO routine. **
** 29 Aug 95 37.12 Removed PC-Relative optimizer option to prevent **
** incorrect conversion to a 68020+ instruction. This **
** should fix the problems on 68000 machines. **
** 27 Aug 95 37.12 Added I/O completion status to each command routine **
** and removed unnecessary code from BeginIO routine. **
** 27 Aug 95 37.12 Re-added list arbitration in cmd_Read. **
** 24 Aug 95 37.11 Isolated SNMA directives to allow assembly with **
** SAS/C assembler. **
** 23 Aug 95 37.11 Add code to correct bug in NComm. Define symbol **
** NCOMM and reassemble to get this support. **
** 23 Aug 95 37.11 Check SOFTINT prior to TBE to keep 8n1 from blocking **
** them. **
** 23 Aug 95 37.11 Insure that interrupts are enabled prior to checking **
** TBE or RBF interrupts. **
** 22 Aug 95 37.11 Turn Handshake on by default to match SerFlags. **
** 21 Aug 95 37.11 Use current baud and buffer sizes for initializing **
** I/O request instead of default. **
** 21 Aug 95 37.11 Set default SERFLAGS during device open. **
** 20 Aug 95 37.11 Clear SERB_ACTIVE IO_FLAG when I/O has completed. **
** 20 Aug 95 37.11 Removed unneeded RTS line code added on 04-Aug-95. **
** 15 Aug 95 37.11 Added line to turn on the DSR bit if needed. **
** Normally commented out. **
** 11 Aug 95 37.10 Second Aminet release! **
** 10 Aug 95 37.10 Added storage arbitration code to cmd_Read and **
** sdcmd_SetParams. **
** 09 Aug 95 37.10 Removed CIAF_PRTRSEL from PRTMASK to allow usage of **
** ring indicator line. **
** 06 Aug 95 37.10 Added "E-" to SNMAOPT line to prevent "Xref in **
** executable" message. **
** 06 Aug 95 37.10 Changed "8n1Base" to "Base8n1" to make more **
** compatible with other assemblers. **
** 06 Aug 95 37.10 Removed ".w" from BTST instructions. **
** 06 Aug 95 37.10 Corrected long word test of byte sized field. **
** 04 Aug 95 37.9 First Aminet Release! **
** 04 Aug 95 37.9 Removed some testing code and prevented unnecessary **
** RTS line updates. **
** 02 Aug 95 37.8 Added the ability to send BREAKs. **
** 29 Jul 95 37.7 Rearranged device init data to allow proper display **
** by VERSION command. (The device base init data had **
** to be within the checksummed area.) **
** 28 Jul 95 37.6 When device was opened multiple times in shared **
** mode, GURU occured on first close. **
** 22 Jul 95 37.5 Corrected version number. 8n1.i was not being **
** generated correctly. **
** 22 Jul 95 37.5 Incorrect handling of interrupts caused TBE **
** interrupts to be missed. **
** 13 Jul 95 37.4 Rewrote the CMD_WRITE and TBE interrupt routines. **
** Lengths of -1 were not being handled properly. **
** 13 Jul 95 37.3 Rewrote OpenDevice() and CloseDevice() calls to **
** correct several problems. **
** 11 Jul 95 37.2 Added "no handshaking" support. **
** 11 Jul 95 37.1 Rewrote the RBF interrupt routines and offloaded **
** some of the work to a PORTS interrupt to prevent **
** overruns and add the EOFMODE option. **
** 07 Jul 95 37.0 Only tried to remove the 'serial.device' instead of **
** removing the CURRENT owner of the serial resources. **
** 06 Jul 95 37.0 Invalid size used for GetPrefs() call causing stack **
** overlay. **
** 04 Jul 95 37.0 Set defaults during OpenDevice() instead of assuming **
** values in I/O request are correct. **
** 04 Jul 95 37.0 Initial Version **
** **
** $Revision Header End *******************************************************
;
; SNMA specific options
;
IFD SNMA
;
CPU M68010
;
SNMAOPT Q,A,M,T,E-
;
INCDIR "include:"
;
ENDC
;
;
;
DEBUG SET 0
;
;
;
SECTION text,CODE
;
;
;
DEVICES_SERIAL_I_OBSOLETE EQU 1
;
;
;
INCLUDE "exec/lists.i"
INCLUDE "exec/memory.i"
INCLUDE "exec/resident.i"
INCLUDE "exec/devices.i"
INCLUDE "exec/execbase.i"
INCLUDE "exec/io.i"
INCLUDE "exec/ports.i"
INCLUDE "exec/errors.i"
INCLUDE "exec/initializers.i"
INCLUDE "intuition/preferences.i"
INCLUDE "devices/timer.i"
INCLUDE "devices/serial.i"
INCLUDE "hardware/custom.i"
INCLUDE "hardware/cia.i"
INCLUDE "hardware/intbits.i"
INCLUDE "hardware/adkbits.i"
INCLUDE "resources/misc.i"
INCLUDE "exec/alerts.i"
INCLUDE "exec/macros.i"
INCLUDE "8n1.i"
;
; Define hardware references
;
XREF _AbsExecBase
XREF _custom
XREF _intena,_intenar,_intreq,_intreqr
XREF _ciab,_ciabpra
XREF _serper,_serdat,_serdatr
XREF _adkcon,_adkconr
;
; Exec Functions
;
XREF _LVORemDevice,_LVOOpenDevice,_LVOCloseDevice
XREF _LVOSupervisor
XREF _LVORemove,_LVORemHead,_LVOAddTail
XREF _LVOAllocMem,_LVOFreeMem,_LVOCopyMem
XREF _LVOReplyMsg
XREF _LVOSendIO,_LVOAbortIO
XREF _LVODisable,_LVOEnable
XREF _LVOFindName
XREF _LVOOpenResource
XREF _LVOAddIntServer,_LVORemIntServer
XREF _LVOSetIntVector
XREF _LVOAlert
XREF _LVOOldOpenLibrary,_LVOCloseLibrary
;
; Misc Resource Functions
;
XREF _LVOAllocMiscResource,_LVOFreeMiscResource
;
; Intuition Functions
;
XREF _LVOGetPrefs
;
; Mask used to get rid of the printer bits.
;
PRTMASK EQU (CIAF_PRTRPOUT|CIAF_PRTRBUSY)
;
; Autovector offsets
;
LVL1VEC EQU (1-1)*4+$64
LVL5VEC EQU (5-1)*4+$64
;
; Device base
;
STRUCTURE Base8n1,LIB_SIZE
UBYTE vb_SaveDDRA
UBYTE vb_SavePRA
APTR vb_MiscBase
APTR vb_OldLevel1
APTR vb_OldLevel5
ULONG vb_SegList
ULONG vb_DefBaud
ULONG vb_DefRBufLen
ULONG vb_CurRBuf
ULONG vb_CurRBufLen
ULONG vb_CurBaud
UBYTE vb_SerFlags
UBYTE vb_Initialized
LABEL sizeof_Base8n1
;
;
;
DISABLE MACRO
IFNC '\1',''
move.w #INTF_\1,_intena
ELSE
jsr _LVODisable(a6)
ENDC
ENDM
;
ENABLE MACRO
IFNC '\1',''
move.w #INTF_SETCLR|INTF_\1,_intena
ELSE
jsr _LVOEnable(a6)
ENDC
ENDM
;
;
;
PUTDEBUG macro ;[msg]
ifge DEBUG-2
movem.l a0/a1/d0/d1,-(sp)
lea (.msg\@,pc),a0 ;Point to static format string
lea (4*4,sp),a1 ;Point to args
XREF DPutFmt
bsr DPutFmt
movem.l (sp)+,d0/d1/a0/a1
bra.b .end\@
.msg\@ dc.b \1,10,0
CNUL 0,4
.end\@
endc
endm
;
;
;
DEBUG1 MACRO
IFEQ DEBUG-1
XREF _SendText
move.l \2,-(sp)
pea.l debug1s\@$
pea.l Name
jsr _SendText(pc)
lea.l 12(sp),sp
bra.b debug1x\@$
debug1s\@$:
dc.b \1,0
CNOP 0,2
debug1x\@$:
ENDC
ENDM
DEBUG0 MACRO
XREF _SendText
pea.l debug1s\@$
pea.l Name
jsr _SendText(pc)
lea.l 8(sp),sp
bra.b debug1x\@$
debug1s\@$:
dc.b \1,0
debug1x\@$:
ENDM
DEBUGIO MACRO
IFEQ DEBUG-1
lea.l \1,a0
jsr printIO
ENDC
ENDM
;
;
;
Start:
moveq #-1,d0 ; set return code
rts ; return
;
; RamLib looks for this romtag
;
ROMTag DC.W RTC_MATCHWORD ; RT_MATCHWORD
DC.L ROMTag ; RT_MATCHTAG
DC.L ENDTag ; RT_ENDSKIP
DC.B RTF_AUTOINIT ; RT_FLAGS
DC.B VERSION ; RT_VERSION
DC.B NT_DEVICE ; RT_TYPE
DC.B 0 ; RT_PRI
DC.L Name ; RT_NAME
DC.L IdString ; RT_IDSTRING
DC.L Init ; RT_INIT
;
; Perform device initialization
;
InitRoutine:
exg d0,a0 ; swap seglist and base
move.l d0,vb_SegList(a0) ; store seglist in base
move.l a6,SysBase ; store in global storage
exg a0,d0 ; swap them back
rts ; return
;
;
;
dev_Open:
DEBUG1 "Devstart %lx",#Start
move.l a5,-(sp) ; save registers
movea.l a6,a5 ; save base
movea.l SysBase(pc),a6 ; get ExecBase
;
tst.l d0 ; unit 0 specified?
bne.b 50$ ; nope, error (who cares?)
;
tst.w LIB_OPENCNT(a5) ; currently open?
bne.b 10$ ; yep, go process
;
move.b IO_SERFLAGS(a1),vb_SerFlags(a5) ; save flags
;
tst.b vb_Initialized(a5) ; already initialized?
bne.b 40$ ; yep, skip initialization
;
bsr initResources ; go alloc resources
tst.l d0 ; initialized?
bne.b 50$ ; nope, error
;
bra.b 40$ ; go exit
;
10$ moveq #SerErr_DevBusy,d0 ; preset error status
btst.b #SERB_SHARED,vb_SerFlags(a5) ; opened shared?
beq.b 50$ ; nope, error
btst.b #SERB_SHARED,IO_SERFLAGS(a1) ; requesting shared?
beq.b 50$ ; nope, error
;
; Initialize I/O request
;
40$ moveq #8,d0 ; get char size
move.b d0,IO_READLEN(a1) ; set read length
move.b d0,IO_WRITELEN(a1) ; set write length
moveq #1,d0 ; get stop bits
move.b d0,IO_STOPBITS(a1) ; set stop bits
ori.b #SERF_XDISABLED|SERF_RAD_BOOGIE|SERF_QUEUEDBRK|SERF_7WIRE,IO_SERFLAGS(a1) ;flags
andi.b #~(SERF_PARTY_ODD|SERF_PARTY_ON),IO_SERFLAGS(a1) ; not used
move.l vb_CurBaud(a5),IO_BAUD(a1) ; set baud
move.l vb_CurRBufLen(a5),IO_RBUFLEN(a1) ; set read buffer length
;
addq.w #1,LIB_OPENCNT(a5) ; incr open count
bclr.b #LIBB_DELEXP,LIB_FLAGS(a5) ; clear expunge bit
moveq #0,d0 ; no error
;
50$ move.b d0,IO_ERROR(a1) ; store error code
movea.l a5,a6 ; restore base
move.l (sp)+,a5 ; restore registers
rts ; return
;
; Attempt to allocate one of the serial resources.
;
allocResource:
move.l a6,-(sp) ; save base pointer
move.l d0,-(sp) ; save unit
lea.l Name(pc),a1 ; get lock name
move.l vb_MiscBase(a5),a6 ; get MiscBase
jsr _LVOAllocMiscResource(a6) ; go allocate it
tst.l d0 ; did we get it?
beq.b 20$ ; yep, branch
;
; It's in use, so we try to locate the device using the string
; returned and attempt to remove it.
;
movea.l SysBase(pc),a6 ; get ExecBase
;
movea.l d0,a1 ; get ptr to serial name
lea.l DeviceList(a6),a0 ; get ptr to device list
jsr _LVOFindName(a6) ; go find it
tst.l d0 ; found?
beq.b 10$ ; nope, branch
;
movea.l d0,a1 ; xfer device ptr
jsr _LVORemDevice(a6) ; remove it
;
; We then retry the allocate.
;
10$ move.l (sp),d0 ; get unit
lea.l Name(pc),a1 ; get lock name
movea.l vb_MiscBase(a5),a6 ; get MiscBase
jsr _LVOAllocMiscResource(a6) ; go allocate it
;
20$ addq.l #4,sp ; restore stack ptr
movea.l (sp)+,a6 ; restore base ptr
rts ; return
;
;
;
initResources:
move.l a1,-(sp) ; save register
;
lea.l miscresource(pc),a1 ; ptr to resource name
jsr _LVOOpenResource(a6) ; go open it
move.l d0,vb_MiscBase(a5) ; save base
;
moveq #MR_SERIALPORT,d0 ; set unit number
bsr.b allocResource ; go allocate it
tst.l d0 ; did we get it?
bne.b 20$ ; nope, error
;
moveq #MR_SERIALBITS,d0 ; set unit number
bsr.b allocResource ; go allocate it
tst.l d0 ; did we get it?
bne.b 10$ ; nope, error
;
bsr getPrefs ; get default preferences
tst.l d0 ; got 'em?
beq.b 30$ ; yep, branch
;
moveq #MR_SERIALPORT,d0 ; set unit
movea.l vb_MiscBase(a5),a6 ; get MiscBase
jsr _LVOFreeMiscResource(a6) ; release the resource
;
10$ moveq #MR_SERIALPORT,d0 ; set unit
movea.l vb_MiscBase(a5),a6 ; get MiscBase
jsr _LVOFreeMiscResource(a6) ; release the resource
movea.l SysBase(pc),a6 ; restore ExecBase
;
20$ moveq #SerErr_DevBusy,d0 ; set error status
bra 40$ ; go return
;
30$ moveq #0,d1 ; clear flags
moveq #UNIT_VBLANK,d0 ; set unit
lea.l timerReq(pc),a1 ; ptr to timer request
lea.l timerdevice(pc),a0 ; ptr to device name
jsr _LVOOpenDevice(a6) ; go open it
;
DISABLE ; disable interrupts
;
lea.l _ciab,a1 ; get ptr to ciab
move.b ciaddra(a1),vb_SaveDDRA(a5) ; save DDR value
andi.b #PRTMASK,ciaddra(a1) ; make serial bits input
move.b ciapra(a1),vb_SavePRA(a5) ; save PR value
ori.b #CIAF_COMDTR|CIAF_COMRTS,ciaddra(a1) ; make DTR/RTS output
andi.b #CIAF_COMCTS|CIAF_COMDSR|PRTMASK,ciapra(a1) ; make CTS/DSR input
andi.b #CIAF_COMDTR|CIAF_COMRTS|PRTMASK,ciaddra(a1) ; turn on DTR/RTS
;
moveq #INTB_PORTS,d0 ; get interrupt number
lea.l VBInterrupt(pc),a1 ; get interrupt ptr
jsr _LVOAddIntServer(a6) ; add it to the list
;
bsr getVBR ; get vector base (in A0)
;
move.l LVL1VEC(a0),vb_OldLevel1(a5) ; save original vector
lea.l level1(pc),a1 ; get new vector ptr
move.l a1,LVL1VEC(a0) ; set new vector
;
move.l LVL5VEC(a0),vb_OldLevel5(a5) ; save original vector
lea.l level5(pc),a1 ; get new vector ptr
move.l a1,LVL5VEC(a0) ; set new vector
;
lea.l _custom,a1 ; get ptr to custom chips
move.w #INTF_RBF|INTF_TBE,intreq(a1) ; clear pending interrupts
move.w #INTF_SETCLR|INTF_RBF|INTF_TBE,intena(a1) ; enable RBF & TBE
;
addq.b #1,vb_Initialized(a5) ; set flag
moveq #0,d0 ; set good status
;
ENABLE ; enable interrupts
;
40$ movea.l (sp)+,a1 ; restore register
rts ; return
;
; Get system preferences
;
getPrefs:
move.l a1,-(sp) ; save registers
;
lea.l intuitlib(pc),a1 ; ptr to library name
jsr _LVOOldOpenLibrary(a6) ; go open it (any version)
movea.l d0,a6 ; get intuition base
;
move.l #(pf_SerParShk+3)&$fffffffc,d0 ; size we need (aligned)
suba.l d0,sp ; reserve space
;
movea.l sp,a0 ; set data area ptr
jsr _LVOGetPrefs(a6) ; get preferences
;
movea.l a6,a1 ; get intuition base
movea.l SysBase(pc),a6 ; restore ExecBase
jsr _LVOCloseLibrary(a6) ; close it
;
moveq #$0f,d1 ; set mask
and.b pf_SerStopBuf(sp),d1 ; get bufsize index
addq.l #8,d1 ; calc shift value
moveq #2,d0 ; get 1<<1
lsl.l d1,d0 ; get default bufsize
move.l d0,vb_DefRBufLen(a5) ; and store
;
moveq #0,d1 ; clear upper half
move.w pf_BaudRate(sp),d1 ; get baud rate
add.l d1,d1 ; generate offset
move.w baudTable(pc,d1.w),d1 ; get default baud
move.l d1,vb_DefBaud(a5) ; and store
;
bsr internalReset ; go init baud and buffer
;
lea.l (pf_SerParShk+3)&$fffffffc(sp),sp ; restore stack
movea.l (sp)+,a1 ; restore registers
rts ; return ( status in D0 )
;
; Preferences baud lookup table
;
baudTable:
dc.w 112,300,1200,2400,4800,9600,19200,31250
;
; Set exception vectors
;
getVBR:
move.l a5,-(sp) ; save registers
suba.l a0,a0 ; ptr to vector base (68000)
btst.b #AFB_68010,AttnFlags+1(a6) ; 68010 or higher?
beq.b 10$ ; nope, go set vector
lea.l 20$(pc),a5 ; ptr to routine
jsr _LVOSupervisor(a6) ; get into supervisor state
10$ movea.l (sp)+,a5 ; restore register
rts ; return
20$ movec.l vbr,a0 ; get vector base
rte ; return
;
;
;
freeResources:
move.l a1,-(sp) ; save registers
;
DISABLE ; disable interrupts
;
bsr getVBR ; get vector base (in A0)
;
moveq #1,d0 ; set not restored code
;
lea.l level1(pc),a1 ; get our vector ptr
cmpa.l LVL1VEC(a0),a1 ; do they match?
bne 10$ ; nope, can't restore
;
lea.l level5(pc),a1 ; get our vector ptr
cmpa.l LVL5VEC(a0),a1 ; do they match?
bne 10$ ; nope, can't restore
;
move.l vb_OldLevel1(a5),LVL1VEC(a0) ; restore original vector
move.l vb_OldLevel5(a5),LVL5VEC(a0) ; restore original vector
;
moveq #INTB_PORTS,d0 ; get interrupt number
lea.l VBInterrupt(pc),a1 ; get interrupt ptr
jsr _LVORemIntServer(a6) ; remove it from the list
;
move.w #INTF_RBF|INTF_TBE,d0 ; indicate serial interrupts
move.w d0,_intena ; disable interrupts
move.w d0,_intreq ; clear pending interrupts
;
bsr freeBuf ; free allocated buffers
;
lea.l _ciab,a0 ; get pointer to ciab
move.b ciaddra(a0),d0 ; get DDR value
andi.b #PRTMASK,d0 ; save printer bits
ori.b #~PRTMASK,d0 ; set serial bits to output
move.b d0,ciaddra(a0) ; store value
;
move.b ciapra(a0),d0 ; get PR value
andi.b #PRTMASK,d0 ; mask out serial bits
move.b vb_SavePRA(a5),d1 ; get saved value
andi.b #~PRTMASK,d1 ; mask out printer bits
or.b d1,d0 ; combine the two
move.b d0,ciapra(a0) ; and store
;
move.b ciaddra(a0),d0 ; get DDR value
andi.b #PRTMASK,d0 ; mask out serial bits
move.b vb_SaveDDRA(a5),d1 ; get saved value
andi.b #~PRTMASK,d1 ; mask out printer bits
or.b d1,d0 ; combine the two
move.b d0,ciaddra(a0) ; and store
;
lea.l timerReq(pc),a1 ; get ptr to timer request
jsr _LVOCloseDevice(a6) ; go close it
;
movea.l vb_MiscBase(a5),a6 ; get MiscBase
moveq #MR_SERIALBITS,d0 ; set unit
jsr _LVOFreeMiscResource(a6) ; release the resource
;
moveq #MR_SERIALPORT,d0 ; set unit
jsr _LVOFreeMiscResource(a6) ; release the resource
movea.l SysBase(pc),a6 ; restore ExecBase
;
ENABLE ; enable interrupts
;
subq.b #1,vb_Initialized(a5) ; clear flag
moveq #0,d0 ; free up everything
;
10$ movea.l (sp)+,a1 ; restore registers
rts ; return
;
; Device Close routine
;
dev_Close:
;
moveq #-1,d0 ; invalidate
move.l d0,IO_DEVICE(a1) ; device
;
subq.w #1,LIB_OPENCNT(a6) ; decr open count
bne.b dev_Null ; still open? yep, branch
;
move.l a5,-(sp) ; save registers
movea.l a6,a5 ; save base
movea.l SysBase(pc),a6 ; get ExecBase
;
bsr freeResources ; free allocated resources
;
movea.l a5,a6 ; restore base
movea.l (sp)+,a5 ; restore registers
;
tst.l d0 ; freed?
bne.b dev_Null ; nope, can't expunge, exit
;
clr.b vb_SerFlags(a6) ; clear flags
;
btst.b #LIBB_DELEXP,LIB_FLAGS(a6) ; delayed expunge set?
beq.b dev_Null ; nope, go exit
;
; Device Expunge routine (also fall through from dev_Close)
;
dev_Expunge:
bset.b #LIBB_DELEXP,LIB_FLAGS(a6) ; Set expunge flag
tst.w LIB_OPENCNT(a6) ; currently open?
bne.b dev_Null ; yep, so just exit
;
move.l vb_SegList(a6),d0 ; get seglist ptr
movem.l d0/a5/a6,-(sp) ; save registers (save D0!)
;
movea.l a6,a5 ; save base
movea.l SysBase(pc),a6 ; get ExecBase
movea.l a5,a1 ; get base
jsr _LVORemove(a6) ; Remove it
;
movea.l a5,a1 ; get base
moveq #0,d0 ; clear work
move.w LIB_NEGSIZE(a5),d0 ; calculate
suba.w d0,a1 ; memory address
add.w LIB_POSSIZE(a5),d0 ; and size
jsr _LVOFreeMem(a6) ; free it
;
movem.l (sp)+,d0/a5/a6 ; restore registers
rts ; return (seglist in D0!)
;
; Device "ExtFunc" routine
;
dev_Null:
moveq #0,d0 ; set return code
rts ; return
;
;
;
IFEQ DEBUG-1
printIO:
movem.l d0-d7/a0-a6,-(sp)
addq.l #1,ioctr
move.l ioctr,-(sp)
move.l a1,-(sp)
move.l a0,-(sp)
pea.l 0$
pea.l Name
DISABLE TBE|INTF_PORTS
jsr _SendText(pc)
ENABLE TBE|INTF_PORTS
lea.l 20(sp),sp
moveq #0,d0
move.w IO_COMMAND(a1),d0
move.l d0,-(sp)
move.l MN_REPLYPORT(a1),-(sp)
moveq #0,d0
move.b LN_TYPE(a1),d0
move.l d0,-(sp)
pea.l 1$
pea.l Name
DISABLE TBE|INTF_PORTS
jsr _SendText(pc)
ENABLE TBE|INTF_PORTS
lea.l 20(sp),sp
move.l IO_LENGTH(a1),-(sp)
moveq #0,d0
move.b IO_ERROR(a1),d0
move.l d0,-(sp)
move.b IO_FLAGS(a1),d0
move.l d0,-(sp)
pea.l 2$
pea.l Name
DISABLE TBE|INTF_PORTS
jsr _SendText(pc)
ENABLE TBE|INTF_PORTS
lea.l 20(sp),sp
move.l IO_EXTFLAGS(a1),-(sp)
move.l IO_RBUFLEN(a1),-(sp)
move.l IO_ACTUAL(a1),-(sp)
pea.l 3$
pea.l Name
DISABLE TBE|INTF_PORTS
jsr _SendText(pc)
ENABLE TBE|INTF_PORTS
lea.l 20(sp),sp
moveq #0,d0
move.b IO_WRITELEN(a1),d0
move.l d0,-(sp)
move.b IO_READLEN(a1),d0
move.l d0,-(sp)
move.l IO_BAUD(a1),-(sp)
pea.l 4$
pea.l Name
DISABLE TBE|INTF_PORTS
jsr _SendText(pc)
ENABLE TBE|INTF_PORTS
lea.l 20(sp),sp
moveq #0,d0
move.w IO_STATUS(a1),d0
move.l d0,-(sp)
moveq #0,d0
move.b IO_SERFLAGS(a1),d0
move.l d0,-(sp)
move.b IO_STOPBITS(a1),d0
move.l d0,-(sp)
pea.l 5$
pea.l Name
DISABLE TBE|INTF_PORTS
jsr _SendText(pc)
ENABLE TBE|INTF_PORTS
lea.l 20(sp),sp
movem.l (sp)+,d0-d7/a0-a6
rts
0$ dc.b '%ls - I/O %8lx, Cnt = %ld',0
1$ DC.B 'Type %8ld ReplyPort %8lx Command %8ld',0
2$ DC.B 'Flags %8lx Error %8ld Length %8ld',0
3$ DC.B 'Actual %8ld RBufLen %8ld ExtFlags %8lx',0
4$ DC.B 'Baud %8ld ReadLen %8ld WriteLen %8ld',0
5$ DC.B 'StopBit %8ld SerFlags %8lx Status %8lx',0
;
;
;
abortIOs dc.b 'Abort Start',0
abortIOe dc.b 'Abort End',0
abortIOa dc.b 'Aborting',0
beginIOs dc.b 'Begin Start',0
beginIOe dc.b 'Begin End',0
openIO dc.b 'Open IO',0
replyIO dc.b 'replyIO',0
ds.l 0
ioctr dc.l 0
ENDC
;
cmdTable dc.w cmd_Invalid-cmdTable ; CMD_INVALID
dc.w cmd_Reset-cmdTable ; CMD_RESET
dc.w cmd_Read-cmdTable ; CMD_READ
dc.w cmd_Write-cmdTable ; CMD_WRITE
dc.w cmd_Invalid-cmdTable ; CMD_UPDATE
dc.w cmd_Clear-cmdTable ; CMD_CLEAR
dc.w cmd_Invalid-cmdTable ; CMD_STOP
dc.w cmd_Invalid-cmdTable ; CMD_START
dc.w cmd_Flush-cmdTable ; CMD_FLUSH
dc.w sdcmd_Query-cmdTable ; SDCMD_QUERY
dc.w sdcmd_Break-cmdTable ; SDCMD_BREAK
endTable dc.w sdcmd_SetParams-cmdTable ; SDCMD_SETPARAMS
;
; Device BeginIO routine
;
dev_BeginIO:
move.l a5,-(sp) ; save register
movea.l a6,a5 ; save base
movea.l SysBase(pc),a6 ; get ExecBase
DEBUGIO beginIOs
;
move.b #NT_MESSAGE,LN_TYPE(a1) ; set type
clr.b IO_ERROR(a1) ; clear error
;
andi.b #~(IOSERF_QUEUED|IOSERF_ACTIVE),IO_FLAGS(a1) ; clear flags
;
move.w IO_COMMAND(a1),d0 ; get command
add.w d0,d0 ; multiply by 2
cmpi.w #endTable-cmdTable,d0 ; in range?
bhi.b 30$ ; nope, error
;
move.w cmdTable(pc,d0.w),d0 ; get routine offset
jsr cmdTable(pc,d0.w) ; go do it
tst.l d0 ; I/O completed?
bne.b 19$ ; nope, go return
;
10$ DEBUGIO beginIOe
btst.b #IOB_QUICK,IO_FLAGS(a1) ; need to reply?
bne.b 20$ ; nope, branch
15$ jsr _LVOReplyMsg(a6) ; send it back
;
19$ bclr.b #IOB_QUICK,IO_FLAGS(a1) ; clear quick bit
20$ movea.l a5,a6 ; restore base
movea.l (sp)+,a5 ; restore register
rts ; return
;
30$ move.b #IOERR_NOCMD,IO_ERROR(a1) ; invalid command
bra.b 10$ ; branch
;
; Device AbortIO routine
;
dev_AbortIO:
move.l a5,-(sp) ; save registers
movea.l a6,a5 ; save base
movea.l SysBase(pc),a6 ; get ExecBase
DEBUGIO abortIOs
;
DISABLE ; disable interrupts
;
btst.b #IOSERB_QUEUED,IO_FLAGS(a1) ; queued request?
bne.b 40$ ; yep, branch
;
btst.b #IOSERB_ACTIVE,IO_FLAGS(a1) ; active request?
beq.b 10$ ; nope, just exit
;
move.w IO_COMMAND(a1),d0 ; get command
subq.w #CMD_READ,d0 ; was it a read?
beq.b 20$ ; yep, go process
;
subq.w #CMD_WRITE-CMD_READ,d0 ; was it a write?
beq.b 30$ ; yep, go process
;
subq.w #SDCMD_BREAK-CMD_WRITE,d0 ; was it a break?
beq.b 30$ ; yep, go process
;
; Fall through or enter from below
;
10$ ENABLE ; enable ints and return
15$ movea.l a5,a6 ; restore base
movea.l (sp)+,a5 ; restore registers
rts ; return
;
; Abort an active read request
;
20$ clr.l cr_IOReq ; no longer active
bra.b 50$ ; go set flags
;
; Abort an active write request
;
30$ clr.l cw_Length ; no longer active
clr.l cw_IOReq ; no longer active
move.l cw_Buffer(pc),d0 ; get buffer ptr
sub.l IO_DATA(a1),d0 ; calc number of bytes xfer'd
move.l d0,IO_ACTUAL(a1) ; store
;
; Force a TBE interrupt to get the next write going.
;
move.w #INTF_SETCLR|INTF_TBE,_intreq ; make TBE pending
bra.b 50$ ; go set flags
;
; Remove I/O from queue.
;
40$ move.l a1,-(sp) ; save ptr
jsr _LVORemove(a6) ; remove it
movea.l (sp)+,a1 ; restore ptr
;
; Set error and return I/O
;
50$ move.b #IOERR_ABORTED,IO_ERROR(a1) ; set error code
move.b IO_FLAGS(a1),d1 ; get flags
bset.b #IOSERB_ABORT,d1 ; set abort flag
andi.b #~(IOSERF_QUEUED|IOSERF_ACTIVE),d1 ; clear flags
move.b d1,IO_FLAGS(a1) ; store flags
DEBUGIO abortIOe
btst.b #IOB_QUICK,d1 ; need to reply?
bne.b 10$ ; nope, branch
jsr _LVOReplyMsg(a6) ; send it back
bra.b 10$ ; branch to return
;
; Abort all active/queued commands and reset internal state
;
cmd_Reset:
move.l a1,-(sp) ; save I/O request
bsr.b cmd_Flush ; go abort queued requests
;
DISABLE ; disable interrupts
;
; This must follow the DISABLE
;
exg a5,a6 ; exchange base and ExecBase
;
move.l cr_IOReq(pc),d0 ; active read?
beq.b 10$ ; nope, branch
movea.l d0,a1 ; get I/O request
bsr dev_AbortIO ; go abort it
;
;
;
10$ move.l cw_IOReq(pc),d0 ; active write?
beq.b 20$ ; nope, branch
movea.l d0,a1 ; get I/O request
bsr dev_AbortIO ; go abort it
;
20$ exg a5,a6 ; restore base and ExecBase
movea.l (sp)+,a1 ; restore I/O request
moveq #8,d0 ; get char size
move.b d0,IO_READLEN(a1) ; set read length
move.b d0,IO_WRITELEN(a1) ; set write length
moveq #1,d0 ; get stop bits
move.b d0,IO_STOPBITS(a1) ; set stop bits
move.l vb_DefBaud(a5),IO_BAUD(a1) ; set to default baud
move.l vb_DefRBufLen(a5),IO_RBUFLEN(a1) ; set to default buflen
bsr internalReset ; go set/verify parameters
move.b d0,IO_ERROR(a1) ; set error code
ENABLE ; enable interrupts
;
; Set RC and return
;
moveq #0,d0 ; I/O complete
rts ; return
;
; Abort all "queued" requests, leaving all active alone.
;
cmd_Flush:
movem.l a1/a2,-(sp) ; save registers
DISABLE ; disable interrupts
;
; Abort all queued read requests.
;
lea.l readQ(pc),a2 ; get ptr to read queue
bsr.b 20$ ; branch to abort
;
; Abort all queued write requests.
;
lea.l writeQ(pc),a2 ; get ptr to write queue
bsr.b 20$ ; branch to abort
;
; Enable, restore, and return to caller
;
ENABLE ; enable interrupts
movem.l (sp)+,a1/a2 ; restore registers
;
; Set RC and return
;
moveq #0,d0 ; I/O complete
;
; !!!NOTE!!! In a mad attempt to save 2 bytes, this RTS is used
; by the subroutine below. Why waste 'em? B-)
;
10$ rts ; return ( used below too!! )
;
; Subroutine to remove and reply each I/O request.
;
20$ movea.l a2,a0 ; get list ptr
jsr _LVORemHead(a6) ; get I/O request
tst.l d0 ; end of list?
beq.b 10$ ; yep, branch to return
;
movea.l d0,a1 ; get I/O request
bclr.b #IOSERB_QUEUED,IO_FLAGS(a1) ; no longer queued
moveq #IOERR_ABORTED,d0 ; indicate aborted
move.b d0,IO_ERROR(a1) ; store status
;
jsr _LVOReplyMsg(a6) ; send it back
bra.b 20$ ; continue with next
;
; Process a CMD_READ request.
;
cmd_Read:
;
; Zero length requests just get returned.
;
clr.l IO_ACTUAL(a1) ; clear bytes read
move.l IO_LENGTH(a1),d0 ; get length and test
beq.b 20$ ; yep, leave
;
; This can be used to circumvent a bug in NComm 3.0 which
; references the buffer even when there was nothing read.
;
IFD NCOMM
move.l IO_DATA(A1),a0 ; get data pointer
clr.b (a0) ; clear first byte in buffer
ENDC
;
; The disable counter works just like exec's TDNestCnt field. It's
; initialized to -1. After incrementing, if it is 0, then we
; can attempt to process this request immediately. If it's > 0,
; then we're already disabled and we must queue this request.
;
addq.b #1,disableRead ; incr disable count
bgt.b 50$ ; >0, already disabled
;
; If we're already processing an request, this one has to wait
; until that one is done, so go queue it.
;
move.l cr_IOReq(pc),d1 ; have an active request?
bne.b 50$ ; yep, go queue this one
;
; If we don't have enough bytes to satisfy this request then go
; queue it.
;
cmp.l i_InCnt(pc),d0 ; length > current bytes?
bgt.b 50$ ; yep, go queue it
;
; Setup fields and go copy the data
;
move.l IO_DATA(a1),cr_OutPtr ; get/set output ptr
move.l IO_LENGTH(a1),cr_Length ; get/set output count
bsr copyData ; go copy 'em
moveq #0,d0 ; I/O complete
;
; We're done, so back off the disable counter.
;
10$ subq.b #1,disableRead ; decr disable count
;
; Return to caller
;
20$ rts ; return
;
; Just set flags and queue. The read interrupt will handle it.
;
50$ bset.b #IOSERB_QUEUED,IO_FLAGS(a1) ; indicate queued
;
; Add this request to the end.
;
DISABLE ; disable interrupts
lea.l readQ(pc),a0 ; get pointer to read queue
move.l a1,-(sp) ; save I/O request
jsr _LVOAddTail(a6) ; and queue it
movea.l (sp)+,a1 ; restore I/O request
ENABLE ; enable interrupts
;
; Indicate that this request was not handled immediatly.
;
moveq #1,d0 ; I/O not complete
bra.b 10$ ; branch to return
;
; Process a CMD_WRITE request.
;
cmd_Write:
clr.l IO_ACTUAL(a1) ; clear bytes written
move.l IO_LENGTH(a1),d0 ; get length and test
beq.b wbexit ; yep, leave
;
; Entry point for Break command and fall through from cmd_Write.
;
sdcmd_Break:
;
; Just set flags and queue. The TBE interrupt will handle it.
;
bset.b #IOSERB_QUEUED,IO_FLAGS(a1) ; indicate queued
;
; Protect.
;
DISABLE ; disable interrupts
;
; Add request to end of queue.
;
lea.l writeQ(pc),a0 ; get queue list ptr
move.l a1,-(sp) ; save I/O request
jsr _LVOAddTail(a6) ; and queue it
movea.l (sp)+,a1 ; restore I/O request
;
; If we have an active request, don't force interrupt.
;
move.l cw_IOReq(pc),d0 ; have an active request?
bne.b 10$ ; yep, branch
;
; Force a TBE interrupt to get the writes going.
;
move.w #INTF_SETCLR|INTF_TBE,_intreq ; make TBE pending
;
; Enable, set RC and return to caller.
;
10$ ENABLE ; enable interrupts
moveq #1,d0 ; I/O not complete
wbexit rts ; return
;
; Resets serial read buffer
;
; Since this routine is called internally, it must NOT reference
; the I/O request.
;
cmd_Clear:
DISABLE ; disable interrupts
;
; Load registers
;
move.l vb_CurRBuf(a5),d0 ; get internal buffer ptr
move.l vb_CurRBufLen(a5),d1 ; and internal buffer len
lea.l i_BufPtr(pc),a0 ; get ptr internal control
;
; Initialize global buffer variables
;
move.l d0,(a0)+ ; store buffer ptr
move.l d0,(a0)+ ; set current input ptr
move.l d0,(a0)+ ; set current output ptr
add.l d1,d0 ; add buffer length
move.l d0,(a0)+ ; store ptr to end of buffer
clr.l (a0)+ ; clear byte cnt
move.l vb_CurBaud(a5),d0 ; get internal baud
lsr.l #4,d0 ; divide by 16
sub.l d0,d1 ; subtract from length
move.l d1,(a0) ; set threshold
;
; Enable, set RC and return.
;
ENABLE ; enable interrupts
moveq #0,d0 ; I/O complete
rts ; return
;
;
;
cmd_Invalid
move.b #IOERR_NOCMD,IO_ERROR(a1) ; set bad status
moveq #0,d0 ; I/O complete
rts ; return
;
; Returns number of bytes currently in internal buffer and
; current serial port status.
;
; NOTE: Not completely compatible with standard serial.device
; since it doesn't return the upper byte of IO_STATUS.
;
sdcmd_Query:
DISABLE ; disable interrupts
moveq #0,d0 ; clear d0
move.b _ciabpra,d0 ; get PR register
andi.w #~PRTMASK,d0 ; zap printer bits
;
; Uncomment the following to turn on the DSR bit. This was done
; for a user whose DSR pin did not function.
;
; bclr #CIAB_COMDSR,d0 ; set DSR
;
;
;
move.w d0,IO_STATUS(a1) ; store status
move.l i_InCnt(pc),IO_ACTUAL(a1) ; byte left in buffer
;
; Enable, set RC and return.
;
ENABLE ; enable interrupts
moveq #0,d0 ; I/O complete
rts ; return
;
;
;
sdcmd_SetParams:
;
; Validate the read, write, and stop bit lengths.
;
moveq #8,d0 ; get char length
cmp.b IO_READLEN(a1),d0 ; 8 bit chars for read?
bne.b 40$ ; nope, branch
cmp.b IO_WRITELEN(a1),d0 ; 8 bit chars for write?
bne.b 40$ ; nope, branch
moveq #1,d0 ; get stop bits
cmp.b IO_STOPBITS(a1),d0 ; 1 stop bit?
bne.b 40$ ; nope, branch
;
; Get and validate the baud rate.
;
move.l IO_BAUD(a1),d1 ; get baud from I/O req
bne.b 20$ ; specified?
move.l vb_CurBaud(a5),d1 ; get current baud from base
bne.b 20$ ; specified?
move.l vb_DefBaud(a5),d1 ; get default baud from base
20$ cmpi.l #110,d1 ; too low?
blt.b 40$ ; error
cmpi.l #292000,d1 ; too high?
bgt.b 40$ ; error
;
; Get and validate the buffer length.
;
move.l IO_RBUFLEN(a1),d0 ; get buffer length
bne.b 30$ ; specified?
move.l vb_CurRBufLen(a5),d0 ; get current from base
bne.b 30$ ; specified?
move.l vb_DefRBufLen(a5),d0 ; get default from base
;
30$ bsr.b internalReset ; go init baud and buffer
35$ move.b d0,IO_ERROR(a1) ; set error code
bne.b 39$
;
; If the 7wire bit is not on, we will only use 3-wire protocol
;
moveq #0,d0 ; clear flag
btst.b #SERB_7WIRE,IO_SERFLAGS(a1) ; use 7wire handshaking?
beq.b 36$ ; nope, branch
moveq #1,d0 ; set flag
36$ move.b d0,Handshake ; store flag
;
; Set RC and return.
;
39$ moveq #0,d0 ; I/O complete
rts ; return
;
; Invalid parm detected.
;
40$ moveq #SerErr_InvParam,d0 ; set error
bra.b 35$ ; go return
;
; Reset the buffer and baud rate
;
; Registers: D0 = Buffer length
; D1 = Baud rate
;
internalReset:
;
; Disable interrupts.
;
DISABLE ; disable interrupts
;
; Save buffer length and go set serper.
;
move.l d0,-(sp) ; save D0
move.l d1,d0 ; get baud rate
bsr.b setPeriod ; go set the serper register
move.l (sp)+,d0 ; restore D0
;
; Determine if the buffer length is adequate for the selected CPS.
; If not, use 64K for the length.
;
move.l vb_CurBaud(a5),d1 ; get current baud
lsr.l #$3,d1 ; divide by 8
cmp.l d1,d0 ; buflen > CPS
bhi.b 10$ ; yep, branch
move.l #65536,d0 ; else use 64K
;
10$ bsr.b allocBuf ; go allocate a new buffer
;
; Enable and return to caller.
;
ENABLE ; enable interrupts
rts ; return (D0 has status)
;
; Set serial period register
;
setPeriod:
cmp.l vb_CurBaud(a5),d0 ; current baud = new baud?
beq.b 40$ ; yep, just exit
move.l d0,vb_CurBaud(a5) ; save new baud
move.l d0,d1 ; save again
lsl.l #3,d0 ; baud *= 8
sub.l d1,d0 ; baud -= saved baud
move.l #25000000,d1 ; get NTSC base
cmpi.b #50,PowerSupplyFrequency(a6) ; PAL machine?
bne.b 10$ ; nope, branch
move.l #24772416,d1 ; get PAL base
10$ cmpi.l #$FFFF,d0 ; Divide
ble.b 20$ ;
lsr.l #5,d0 ;
divu.w d0,d1 ;
andi.l #$FFFF,d1 ;
lsr.l #5,d1 ;
bra.b 30$ ;
20$ divu.w d0,d1 ;
30$ move.w d1,_serper ; set period value
40$ rts
;
; Allocate new internal buffer
;
allocBuf:
cmp.l vb_CurRBufLen(a5),d0 ; len same as previous?
beq.b 10$ ; yep, so no need to alloc
move.l d0,d1 ; save length
movem.l d1/a1,-(sp) ; save registers
moveq #MEMF_PUBLIC,d1 ; public memory
jsr _LVOAllocMem(a6) ; go allocate it
movem.l (sp)+,d1/a1 ; restore registers
tst.l d0 ; did we get it?
beq.b 20$ ; if zero, error
bsr.b freeBuf ; go free previous buffer
move.l d0,vb_CurRBuf(a5) ; store new ptr
move.l d1,vb_CurRBufLen(a5) ; and length
bsr cmd_Clear ; go setup buffer
10$ moveq #0,d0 ; success
rts ; return
20$ moveq #SerErr_BufErr,d0 ; set error status
rts ; return
;
; Free internal buffer
;
freeBuf:
movem.l d0-d1/a0-a1,-(sp) ; save registers
move.l vb_CurRBuf(a5),d0 ; is one there?
beq.b 10$ ; no so branch
movea.l d0,a1 ; get ptr
move.l vb_CurRBufLen(a5),d0 ; get length
clr.l vb_CurRBuf(a5) ; clear
clr.l vb_CurRBufLen(a5) ; clear
jsr _LVOFreeMem(a6) ; free it
10$ movem.l (sp)+,d0-d1/a0-a1 ; restore registers
rts ; return
;
; Checks CTS status and if clear generates a TBE interrupt or
; requeues the timer request.
;
; Entered from Exec using the MsgPort callback.
;
; Input: a6 = ExecBase
; Output: none
;
; No need to preserve d0/d1/a0/a1
;
timerRtn:
lea.l timerReq(pc),a1 ; get ptr to timer request
jsr _LVORemove(a6) ; remove request
;
; If we were breaking, reset adkcon.
;
btst.b #ADKB_UARTBRK,_adkconr ; were we breaking?
beq.b 10$ ; nope, skip reset
;
move.w #ADKF_UARTBRK,_adkcon ; stop breaking
;
;
;
10$ move.b Handshake(pc),d0 ; are we handshaking?
beq.b 20$ ; nope, generate interrupt
btst.b #CIAB_COMCTS,_ciabpra ; clear to send?
beq.b 20$ ; yep, go start writing
;
lea.l timerReq(pc),a1 ; get ptr to timer request
move.l #1000,IOTV_TIME+TV_MICRO(a1) ; wait for .001 seconds
jsr _LVOSendIO(a6) ; go queue it
bra.b 30$ ; branch to return
;
; CTS is clear so generate TBE interrupt to restart writing
;
20$ move.w #INTF_SETCLR|INTF_TBE,_intreq ; set TBE interrupt
30$ rts ; return
;
; Non serial interrupt
;
level1n:
movem.l d0-d1/a0-a1/a5-a6,-(sp) ; save registers
lea _custom,a0 ; get ptr to custom regs
move.w intenar(a0),d1 ; get enabled interrupts
and.w intreqr(a0),d1 ; and in requested interrupts
movea.l SysBase(pc),a6 ; get ExecBase
;
btst #INTB_DSKBLK,d1 ; Disk block done?
beq.b 10$ ; nope, branch
;
movem.l IVDSKBLK(a6),a1/a5 ; get data and code ptrs
pea.l 20$(pc) ; push return address
jmp (a5) ; jump to routine
;
10$ btst #INTB_SOFTINT,d1 ; software interrupt?
beq.b 20$ ; nope, branch
;
movem.l IVSOFTINT(a6),a1/a5 ; get data and code ptrs
pea.l 20$(pc) ; push return address
jmp (a5) ; jump to routine
;
20$ movem.l (sp)+,d0-d1/a0-a1/a5-a6 ; restore registers
rte ; return
;
; Level 1 interrupt handler
;
level1:
btst.b #INTB_INTEN,_intenar ; interrupts enabled?
beq.b 35$ ; nope, ignore
btst.b #INTB_SOFTINT,_intreqr+1 ; software interrupt?
bne.b level1n ; nope, branch
btst.b #INTB_TBE,_intreqr+1 ; xmit buffer empty?
beq.b level1n ; nope, invoke old handler
;
; Handle "Transmit Buffer Empty" interrupt (write)
;
move.w #INTF_TBE,_intreq ; clear interrupt
move.l d0,-(sp) ; save D0 (faster than MOVEM)
move.l a0,-(sp) ; save A0 (faster than MOVEM)
;
; If we're not handshaking, bypass it.
;
10$ move.b Handshake(pc),d0 ; are we handshaking?
beq.b 20$ ; nope, skip CTS test
btst.b #CIAB_COMCTS,_ciabpra ; clear to send?
bne.b 40$ ; nope, branch
;
; If cw_Length goes negative here, we are either done with a
; request or we were called as a result of a fake interrupt
; to force us to get the next request going.
;
20$ subq.l #1,cw_Length ; decr write length
blt.b 60$ ; < zero, done, branch
;
; Currently processing a request.
;
movea.l cw_Buffer(pc),a0 ; get buffer ptr
move.w #256,d0 ; set stop bit
move.b (a0)+,d0 ; get next byte
move.l a0,cw_Buffer ; store buffer ptr
move.w d0,_serdat ; store in serdat reg
;
30$ movea.l (sp)+,a0 ; restore registers
move.l (sp)+,d0 ; restore registers
35$ rte ; return
;
; Queue a timer request to recheck CTS status
;
40$ movem.l d1/a1/a6,-(sp) ; save registers
lea.l timerReq(pc),a1 ; get ptr to timer request
move.l #1000,IOTV_TIME+TV_MICRO(a1) ; wait for .001 seconds
movea.l SysBase(pc),a6 ; get ExecBase
jsr _LVOSendIO(a6) ; queue the request
movem.l (sp)+,d1/a1/a6 ; restore registers
bra.b 30$ ; go return
;
; There aren't anymore requests, so clear and exit
;
50$ clr.l cw_Length-Start(a6) ; clear length
clr.l cw_IOReq-Start(a6) ; clear
movem.l (sp)+,d1/a1/a6 ; restore registers
bra.b 30$ ; go return
;
; Write request completed
;
60$ movem.l d1/a1/a6,-(sp) ; save registers
;
move.l cw_IOReq(pc),d0 ; active I/O request?
beq.b 70$ ; nope, branch
;
; Reply it and setup for next
;
movea.l d0,a1 ; get I/O request
bclr.b #IOSERB_ACTIVE,IO_FLAGS(a1) ; no longer active
clr.b IO_ERROR(a1) ; no error
movea.l SysBase(pc),a6 ; get ExecBase
DEBUGIO replyIO
jsr _LVOReplyMsg(a6) ; return I/O request
;
70$ lea.l Start(pc),a6 ; get section base
;
lea.l writeQ(pc),a1 ; get ptr to write queue
move.l (a1),a0 ; get head of list
move.l (a0),d0 ; get successor
beq.b 50$ ; end of list? yep, branch
;
; Remove the node from the list
;
move.l d0,(a1) ; make new head
exg.l d0,a0 ; swap nodes
move.l a1,LN_PRED(a0) ; store predecessor
;
move.l d0,a1 ; get I/O request
;
; If it's a BREAK, then branch to process as such.
;
cmpi.w #SDCMD_BREAK,IO_COMMAND(a1) ; BREAK command?
beq.b 100$ ; yep, branch
;
; Check for absolute length
;
move.l IO_DATA(a1),a0 ; get data ptr
moveq #-1,d1 ; get value
move.l IO_LENGTH(a1),d0 ; get length
cmp.l d1,d0 ; length = -1?
bne.b 90$ ; nope, skip scan
;
; Scan the data for null to calc the length
;
move.l a0,d1 ; save data ptr
80$ tst.b (a0)+ ; does it equal 0?
bne.b 80$ ; nope, so continue
suba.l d1,a0 ; calc # of bytes+1
move.l a0,d0 ; xfer
subq.l #1,d0 ; calc # of bytes
movea.l d1,a0 ; get data ptr
;
90$ move.l d0,IO_ACTUAL(a1) ; go ahead and set it
bclr.b #IOSERB_QUEUED,IO_FLAGS(a1) ; no longer queued
bset.b #IOSERB_ACTIVE,IO_FLAGS(a1) ; make it active
move.l d0,cw_Length-Start(a6) ; store length
move.l a0,cw_Buffer-Start(a6) ; store buffer ptr
move.l a1,cw_IOReq-Start(a6) ; store I/O request ptr
;
movem.l (sp)+,d1/a1/a6 ; restore registers
bra 10$ ; go start request
;
; Start the BREAK.
;
100$ bclr.b #IOSERB_QUEUED,IO_FLAGS(a1) ; no longer queued
bset.b #IOSERB_ACTIVE,IO_FLAGS(a1) ; make it active
clr.l cw_Length-Start(a6) ; clear length
move.l a1,cw_IOReq-Start(a6) ; store I/O request ptr
move.w #ADKF_SETCLR|ADKF_UARTBRK,_adkcon ; start break
move.l IO_BRKTIME(a1),d0 ; get break time
lea.l timerReq(pc),a1 ; get ptr to timer request
move.l d0,IOTV_TIME+TV_MICRO(a1) ; set the timeout
movea.l SysBase(pc),a6 ; get ExecBase
jsr _LVOSendIO(a6) ; queue the request
movem.l (sp)+,d1/a1/a6 ; restore registers
bra 30$ ; go exit
;
; Non serial interrupt
;
level5n:
movem.l d0-d1/a0-a1/a5-a6,-(sp) ; save registers
lea _custom,a0 ; get ptr to custom regs
move.w intenar(a0),d1 ; get enabled interrupts
and.w intreqr(a0),d1 ; and in requested interrupts
movea.l SysBase(pc),a6 ; get ExecBase
btst #INTB_DSKSYNC,d1 ; Disk synchronized?
beq.b 10$ ; nope, branch
movem.l IVDSKSYNC(A6),a1/a5 ; get data and code ptrs
jsr (a5) ; branch to routine
10$ movem.l (sp)+,d0-d1/a0-a1/a5-a6 ; restore registers
rte ; return
;
; Default Level 5 handler
;
level5:
btst.b #INTB_INTEN,_intenar ; interrupts enabled?
beq.b 45$ ; nope, ignore
btst.b #INTB_RBF,_intreqr ; receive buffer full?
beq.b level5n ; nope, invoke old handler
;
move.l a0,-(sp) ; save registers
;
10$ btst.b #7,_serdatr ; Overrun?
bne.b 50$ ; yep, branch
;
20$ movea.l i_BufIn(pc),a0 ; get current ptr
move.b _serdatr+1,(a0)+ ; store received byte
move.w #INTF_RBF,_intreq ; clear RBF interrupt
addq.l #1,i_InCnt ; incr bytes in buffer
;
cmpa.l i_BufEnd(pc),a0 ; hit end of buffer?
beq.b 60$ ; yep, branch
30$ move.l a0,i_BufIn ; store input ptr
;
subq.l #1,i_Thresh ; close to full buffer?
beq.b 70$ ; yep, branch
;
40$ btst.b #INTB_RBF,_intreqr ; receive buffer full?
bne.b 10$ ; yep, go get another byte
;
movea.l (sp)+,a0 ; restore registers
45$ rte ; return
;
; We've missed some data, so set overrun flag.
;
50$ addq.b #1,Overrun ; set overrun flag
bra.b 20$ ; continue
;
; Hit physical end of buffer, so wrap to the start of the buffer.
;
60$ movea.l i_BufPtr(pc),a0 ; get buffer ptr
bra.b 30$ ; continue
;
; Hit buffer threshold, so tell other end not to send any more
; data.
;
70$ tst.b Handshake ; are we handshaking?
beq.b 40$ ; nope, skip RTS
bset.b #CIAB_COMRTS,_ciabpra ; block further input
bra.b 40$ ; go return
;
;
;
level2:
;
; If there's nothing in the buffer, there's no point in going
; any further.
;
move.l i_InCnt(pc),d0 ; anything in the buffer?
bne.b 20$ ; yep, branch
10$ moveq #0,d0 ; set Z flag
rts ; return
;
; If we've been "disabled" then get out.
;
20$ move.b disableRead(pc),d0 ; internally disabled?
bge.b 10$ ; yep, get out of here
lea.l Start(pc),a5 ; get base
;
; If we have an active request, branch down and try to fulfill it.
;
move.l cr_IOReq(pc),d0 ; get and test active I/O
bne.b 30$ ; nzero, active, branch
;
; Get first node in list and test if empty.
;
move.l (a1),a0 ; get head of list
move.l (a0),d0 ; get successor
beq.b 10$ ; end of list? yep, branch
;
; Remove the node from the list
;
move.l d0,(a1) ; make new head
exg.l d0,a0 ; swap nodes
move.l a1,LN_PRED(a0) ; store predecessor
;
; Setup fields for processing a read request
;
movea.l d0,a1 ; get I/O request
bclr.b #IOSERB_QUEUED,IO_FLAGS(a1) ; no longer queued
bset.b #IOSERB_ACTIVE,IO_FLAGS(a1) ; make it active
move.l a1,cr_IOReq-Start(a5) ; store I/O request
move.l IO_DATA(a1),cr_OutPtr-Start(a5) ; get/set output ptr
move.l IO_LENGTH(a1),cr_Length-Start(a5) ; get/set output count
;
; Process an active I/O request
;
30$ movea.l SysBase(pc),a6 ; get ExecBase
move.l d0,a1 ; get I/O request
bsr copyData ; go copy 'em
tst.l d0 ; done with request?
beq.b 10$ ; nope, branch
;
; the request has been satisfied, so return it.
;
clr.l cr_IOReq-Start(a5) ; clear request
bclr.b #IOSERB_ACTIVE,IO_FLAGS(a1) ; no longer active
DEBUGIO replyIO
jsr _LVOReplyMsg(a6) ; return I/O
bra.b 10$
;
; Registers:
; Entry: A1 Ptr to I/O Request
; A6 ExecBase
; Exit: D0 ZERO - request not done, do not reply it
; NZERO - request done, reply it
;
copyData:
;
; Process an active I/O request (or fall through from above)
;
movem.l d2-d5/a1-a5,-(sp) ; save registers
lea.l Start(pc),a5 ; get base
movea.l a1,a4 ; get I/O request
;
movea.l i_BufOut(pc),a2 ; get current bufout ptr
movea.l cr_OutPtr(pc),a3 ; get current output ptr
move.l cr_Length(pc),d2 ; get current bytes needed
move.l i_InCnt(pc),d3 ; get current bytes in buffer
;
; If we don't have enough bytes to satisfy the request,
; set the length to the number of bytes we do have.
;
cmp.l d2,d3 ; enuf to satisfy request?
bge.b 15$ ; yep, so branch
move.l d3,d2 ; # to copy = # in buffer
bra.b 15$ ; branch to loop entry
;
; Start of copy loop.
;
10$ movea.l i_BufPtr(pc),a2 ; reset bufout to start
;
; Entry point of copy loop.
;
15$ move.l d2,d3 ; xfer # of bytes to copy
;
; If the copy will extend past the end of the buffer, we can
; only copy the number of bytes to the end this go around.
;
move.l i_BufEnd(pc),d0 ; get ptr to end of buf
sub.l a2,d0 ; calc # of bytes to end
cmp.l d0,d3 ; # to copy < # to end?
blt.b 20$ ; yep, branch
move.l d0,d3 ; get bytes to end
;
; Registers:
;
; A2 = pointer from which data will be copied
; A3 = pointer to which data will be copied
; D2 = number of bytes that need to be copied
; D3 = number of bytes to copy this iteration
;
20$ btst.b #SERB_EOFMODE,IO_SERFLAGS(a4) ; EOFMODE requested?
beq.b 30$ ; nope, just go copy
;
; EOFMODE was specified so copy characters 1 at a time until
; we hit an EOF character, the output butter has filled, or
; the input buffer has drained.
;
move.l d3,d0 ; get length
;
21$ move.b (a2)+,d1 ; get byte
move.b d1,(a3)+ ; put in output buffer
;
lea.l IO_TERMARRAY(a4),a1 ; get termarry ptr
cmp.b (a1)+,d1 ; found term char?
bge.b 22$ ; possibly, branch
cmp.b (a1)+,d1 ; found term char?
bge.b 22$ ; possibly, branch
cmp.b (a1)+,d1 ; found term char?
bge.b 22$ ; possibly, branch
cmp.b (a1)+,d1 ; found term char?
bge.b 22$ ; possibly, branch
cmp.b (a1)+,d1 ; found term char?
bge.b 22$ ; possibly, branch
cmp.b (a1)+,d1 ; found term char?
bge.b 22$ ; possibly, branch
cmp.b (a1)+,d1 ; found term char?
bge.b 22$ ; possibly, branch
cmp.b (a1)+,d1 ; found term char?
bgt.b 23$ ; nope, branch
22$ beq.b 24$ ; term char found?
;
; Didn't find a term character, so continue with the copy loop
;
23$ subq.l #1,d0 ; decr length counter
bne.b 21$ ; continue if more
bra.b 40$ ; done with copy, branch
;
; We've found a termination character.
;
24$ clr.l cr_Length-Start(a5) ; done with request
bra.b 50$ ; branch
;
; EOFMODE not specified, so just do a bulk copy.
;
; XXX POSSIBLE SPEEDUP XXX
;
; For short copies it would be quicker to have a simple inline
; loop, but what's short??? It would be different by CPU.
;
30$ move.l d3,d0 ; get length
movea.l a3,a1 ; where to put it
movea.l a2,a0 ; where to get it
jsr _LVOCopyMem(a6) ; copy it
adda.l d3,a2 ; update bufout
adda.l d3,a3 ; update outptr
;
; Fall through and entered from EOFMODE loop
;
; If the following calculation results in a value greater than
; zero, then we have a buffer wrap and need to process the
; remaining bytes at the beginning of the buffer.
;
40$ sub.l d3,d2 ; calc bytes left to copy
bgt 10$ ; >0, more to copy, branch
;
;
;
50$ move.l a3,d1 ; get outptr
sub.l cr_OutPtr(pc),d1 ; calc length
move.l a3,cr_OutPtr-Start(a5) ; update outptr
;
; Update output ptr and I/O request
;
move.l a2,i_BufOut-Start(a5) ; store bufout ptr
add.l d1,IO_ACTUAL(a4) ; update I/O request
;
; Update number of bytes left in the buffer.
;
sub.l d1,i_InCnt-Start(a5) ; calc bytes left in buffer
;
; If the threshold becomes positive here, then, if requested,
; tell the other end that it's okay to start sending more data.
;
add.l d1,i_Thresh-Start(a5) ; calc thresh and test
ble.b 60$ ; <= 0, need more data, skip
tst.b Handshake-Start(a5) ; are we handshaking?
beq.b 60$ ; nope, skip RTS
bclr.b #CIAB_COMRTS,_ciabpra ; ready to receive more data
;
; Set error if we've had any overruns.
;
60$ tst.b Overrun-Start(a5) ; did overrun occur?
beq.b 70$ ; nope, branch
clr.b Overrun-Start(a5) ; reset overrun flag
move.b #SerErr_LineErr,IO_ERROR(a4) ; set error code
;
; Calc number of bytes left to copy. If the result is greater
; than zero, then we have more to copy so return an incomplete
; status.
;
70$ moveq #0,d0 ; assume I/O incomplete
sub.l d1,cr_Length-Start(a5) ; update length and test
bgt.b 90$ ; >0, more to do, branch
;
; We've completed the I/O request either by copying the requested
; of bytes or by finding an EOFMODE character, so return a "reply"
; status.
;
moveq #1,d0 ; indicate reply
;
; Restore and exit
;
90$ movem.l (sp)+,d2-d5/a1-a5 ; restore registers
rts ; return (status in D0)
;
; Align data
;
CNOP 0,4
;
;
;
Init:
DC.L sizeof_Base8n1
DC.L funcTab
DC.L dataTab
DC.L InitRoutine
;
;
;
funcTab:
DC.W -1
DC.W dev_Open-funcTab
DC.W dev_Close-funcTab
DC.W dev_Expunge-funcTab
DC.W dev_Null-funcTab
DC.W dev_BeginIO-funcTab
DC.W dev_AbortIO-funcTab
DC.W -1
;
;
;
dataTab:
INITBYTE LN_TYPE,NT_DEVICE
INITLONG LN_NAME,Name
INITBYTE LIB_FLAGS,LIBF_SUMUSED|LIBF_CHANGED
INITWORD LIB_VERSION,VERSION
INITWORD LIB_REVISION,REVISION
INITLONG LIB_IDSTRING,IdString
DC.W 0
;
; String Constants
;
miscresource:
DC.B "misc.resource",0
timerdevice:
DC.B "timer.device",0
intuitlib:
DC.B "intuition.library",0
Name:
DC.B "8n1.device",0
IdString:
VSTRING
;
; End of checksummed area. (Realigns data too!)
;
ENDTag:
CNOP 0,4
;
; Global SysBase (Use instead of AbsExecBase for speed)
;
SysBase:
DC.L 0
;
; Internal buffer tracking (DO NOT CHANGE THE ORDER!!!!)
;
i_BufPtr:
DC.L 0
i_BufIn:
DC.L 0
i_BufOut:
DC.L 0
i_BufEnd:
DC.L 0
i_InCnt:
DC.L 0
i_Thresh:
DC.L 0
;
; Used while processing a read request.
;
cr_IOReq:
DC.L 0
cr_OutPtr:
DC.L 0
cr_Length:
DC.L 0
;
; List head for read requests
;
readQ:
DC.L readQ+MLH_TAIL
DC.L 0
DC.L readQ
;
; Write control.
;
cw_Length:
DC.L 0
cw_Buffer:
DC.L 0
cw_IOReq:
DC.L 0
;
; List head for write requests
;
writeQ:
DC.L writeQ+MLH_TAIL
DC.L 0
DC.L writeQ
;
;
;
timerPort:
DC.L 0 ; LN_SUCC
DC.L 0 ; LN_PRED
DC.B NT_MSGPORT ; LN_TYPE
DC.B 0 ; LN_PRI
DC.L 0 ; LN_NAME
DC.B 3 ; MP_FLAGS (undoc'ed)
DC.B 0 ; MP_SIGBIT
DC.L timerRtn ; MP_SIGTASK
DC.L timerPort+MP_MSGLIST+LH_TAIL ; LH_HEAD
DC.L 0 ; LH_TAIL
DC.L timerPort+MP_MSGLIST ; LH_TAILPRED
DC.B 0 ; LH_TYPE
DC.B 0 ; LH_pad
DC.W 0 ; long align
;
;
;
timerReq:
DC.L 0 ; LN_SUCC
DC.L 0 ; LN_PRED
DC.B NT_MESSAGE ; LN_TYPE
DC.B 0 ; LN_PRI
DC.L 0 ; LN_NAME
DC.L timerPort ; MN_REPLYPORT
DC.W IOTV_SIZE ; MN_LENGTH
DC.L 0 ; IO_DEVICE
DC.L 0 ; IO_UNIT
DC.W TR_ADDREQUEST ; IO_COMMAND
DC.B 0 ; IO_FLAGS
DC.B 0 ; IO_ERROR
DC.L 0 ; TV_SECS
DC.L 0 ; TV_MICROS
;
;
;
VBInterrupt:
DC.L 0 ; LN_SUCC
DC.L 0 ; LN_PRED
DC.B NT_INTERRUPT ; LN_TYPE
DC.B 0 ; LN_PRI
DC.L Name ; LN_NAME
DC.L readQ ; IS_DATA
DC.L level2 ; IS_CODE
;
; Global flags
;
Overrun:
DC.B 0
Handshake:
DC.B 1
disableRead:
DC.B -1
;
;
;
END